<!DOCTYPE html>
<html class="no-js" lang="zh">
  <head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="alternate" href="https://www.desgard.com" hreflang="pt-BR">
<link href="http://gmpg.org/xfn/11" rel="profile">
<meta name="HandheldFriendly" content="True">
<meta name="MobileOptimized" content="320">
<meta name="theme-color" content="#005344">
<title>Guardia · 瓜地</title>
<meta name="google-site-verification" content="zS1dSn20XtA4FJYEOQLXqI0boxZdMnJ2g3beje-cl20">
<meta name="description" content="浅谈ObjC引用计数">
<meta name="keywords" content="">
<!-- Social: Facebook / Open Graph -->
<meta property="og:url" content="https://www.desgard.com/Reference-Count-Analysis/">
<meta property="og:title" content="       Reference Counting Brief Analysis | Gua  ">
<meta property="og:description" content="浅谈ObjC引用计数">
<meta property="og:site_name" content="Desgard_Duan">
<meta property="og:locale" content="pt_BR">
<meta property="og:type" content="website">
<meta property="og:author" content="https://www.facebook.com/desgard.duan">
<meta property="og:image" content="https://www.desgard.com">
<!-- Social: Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@nandomoreirame">
<meta name="twitter:domain" content="https://www.desgard.com">
<meta name="twitter:title" content="       Reference Counting Brief Analysis | Gua  ">
<meta name="twitter:description" content="浅谈ObjC引用计数">
<meta name="twitter:image:src" content="https://www.desgard.com">
<!-- Favicons -->
<link rel="apple-touch-icon" sizes="114x114" href="https://www.desgard.com/assets/ico/apple-touch-icon-114-516f4e19976b9e4dbb77ad9b576831fe.png">
<link rel="apple-touch-icon" sizes="72x72" href="https://www.desgard.com/assets/ico/apple-touch-icon-72-5409b2df229305703caf583d86c845ab.png">
<link rel="apple-touch-icon" href="https://www.desgard.com/assets/ico/apple-touch-icon-57-aa873e019cf659e0d4e6a0b5bb9f379d.png">
<link rel="shortcut icon" href="https://www.desgard.com/assets/ico/favicon-4298be3d3fbe23e18d65bded9d930899.png">
<!-- rel prev and next -->
<!-- Canonical link tag -->
<link rel="canonical" href="https://www.desgard.com/Reference-Count-Analysis/">
<link rel="alternate" type="application/rss+xml" title="Gua" href="https://www.desgard.com/feed.xml">
<link rel="stylesheet" href="/assets/main-0b7b828712f4c43b75bba4861c907bb1.css">
<script src="/assets/modernizr-custom-cb807611a3e262b2eac59444cbab74d6.js" data-cfasync="false"></script>
<script src="/assets/js/jquery-3.1.js"></script>
<script src="/assets/js/jquery.rotate.js"></script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "Website",
  "publisher": "www.desgard.com",
  "url": "http://www.desgard.com/",
  "description": "瓜地"
}
</script>
<script type="text/javascript">
  var disqus_shortname = 'desgard',
      baseurl          = '';
</script>
<!--
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-52446115-1']);
_gaq.push(['_trackPageview']);
(function() {
  var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
  var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>-->
<script>
var _hmt = _hmt || [];
(function() {
  var hm = document.createElement("script");
  hm.src = "//hm.baidu.com/hm.js?c5a8123bc51782a3083a631ed9ff50e4";
  var s = document.getElementsByTagName("script")[0]; 
  s.parentNode.insertBefore(hm, s);
})();
</script>
<script type="text/x-mathjax-config">
    MathJax.Hub.Config({
      tex2jax: {
        inlineMath: [ ['$','$'] ],
        displayMath: [ ['$$','$$'] ],
        processEscapes: true
      }
    });
  </script>
  </head>
  <body class="post-template single">
    <header class="header">
  <div class="container">
    <h1><a href="/"><strong>desgard</strong>.com</a></h1>
    <nav class="navbar">
      <ul>
        <li><a href="/">Home</a></li>
        <li><a href="#!" data-modal="modalSearch"><i class="fa fa-search"></i></a></li>
        <li><a href="/comment " target="_blank"><i class="fa fa-comments"></i></a></li>
        <li><a href="/feed.xml" target="_blank"><i class="fa fa-feed"></i></a></li>
      </ul>
    </nav>
  </div>
</header>
    <main class="wrapper container" itemprop="mainContentOfPage" itemscope="itemscope" itemtype="http://schema.org/Blog">
      <article class="post" itemscope="itemscope" itemtype="http://schema.org/BlogPosting" itemprop="blogPost">
  <header class="post-header">
    <h1>Reference Counting Brief Analysis</h1>
    <div class="post-meta">
      <time datetime="2015-10-13">
        <i class="fa fa-calendar-o"></i> <time datetime="2016-06-22"> 2016-06-22
      </time>
      <span>
        <i class="fa fa-comments"></i> <a href="#container">Comment me!</a>
      </span>
      <span>
<!--
-->
      </span>
    </div>
  </header>
  <h2 id="begin">Begin</h2>
<p>这篇文不像动画那么有趣。记录的是我在学习Reference Counting中遇到的坑。倘若你之前只是听说过但是没有具体的实践过，我想你在阅读本文后会有很大的收获。</p>
<h2 id="manual-reference-counting-introduce">Manual Reference Counting Introduce</h2>
<p>笔者学习iOS开发一年有余，对于我的iOS入门书籍，是<em>Objective-C Programming: The Big Nerd Ranch Guide</em>，在23.4部分，对Referrence Counting会有介绍。我们对MRC最初的认识就是，<strong>retain计数加一，release计数减一，autorelease计数将来会减一，retainCount可以返回引用计数</strong>。当引用计数减到0时，系统将会自动调用对象的<code>dealloc</code>方法，你可以把它类比成C#中的<code>dispose</code>方法，而原先的开发人员可以在<code>dealloc</code>中释放或清理资源。</p>
<p>也许第一遍看这些知识你正处于一个入门开发学习状态而没有进行试验，当我们深入去学习这门语言之后，你会发现很多问题。接下来，我们一一提出并解决。</p>
<blockquote>
  <p>alloc/retain/release/dealloc是如何实现的？</p>
</blockquote>
<p>由于<strong>Cocoa framework</strong>的闭源，我们只能通过其互换框架<strong><a href="http://gnustep.org/">GNUstep</a></strong>来了解其原理。首先我们通过<code>alloc</code>方法入手。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">+</span> <span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="n">alloc</span> <span class="p">{</span>
    <span class="k">return</span> <span class="o">[</span><span class="nb">self</span> <span class="ss">allocWithZone</span><span class="p">:</span> <span class="no">NSDefaultMallocZone</span><span class="p">()</span><span class="o">]</span><span class="p">;</span>
<span class="p">}</span>
<span class="o">+</span> <span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="ss">allocWithZone</span><span class="p">:</span> <span class="p">(</span><span class="no">NSZone</span> <span class="o">*</span><span class="p">)</span><span class="n">z</span> <span class="p">{</span>
    <span class="k">return</span> <span class="no">NSAllocateObject</span><span class="p">(</span><span class="nb">self</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">z</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p><code>alloc</code>方法会调用<code>NSAllocateObject</code>函数。具体是做什么的呢？我们往后看。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">struct</span> <span class="n">obj_layout</span> <span class="p">{</span>
    <span class="no">NSUInteger</span> <span class="n">retained</span><span class="p">;</span>
<span class="p">};</span>
<span class="n">inline</span> <span class="nb">id</span> <span class="no">NSAllocateObject</span><span class="p">(</span><span class="no">Class</span> <span class="n">aClass</span><span class="p">,</span> <span class="no">NSUInteger</span> <span class="n">extraBytes</span><span class="p">,</span> <span class="no">NSZone</span> <span class="o">*</span><span class="n">zone</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">int</span> <span class="n">size</span> <span class="o">=</span> <span class="sr">/* 计算容纳对象所需要的内存大小 */</span>
    <span class="sr">//</span> <span class="err">分配内存空间</span>
    <span class="nb">id</span> <span class="kp">new</span> <span class="o">=</span> <span class="no">NSZoneMalloc</span><span class="p">(</span><span class="n">zone</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="sr">//</span> <span class="err">空间数据置</span><span class="mi">0</span>
    <span class="n">memset</span><span class="p">(</span><span class="kp">new</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="n">size</span><span class="p">);</span>
    <span class="kp">new</span> <span class="o">=</span> <span class="p">(</span><span class="nb">id</span><span class="p">)</span><span class="o">&amp;</span><span class="p">((</span><span class="n">struct</span> <span class="n">obj_layout</span> <span class="o">*</span><span class="p">)</span> <span class="kp">new</span><span class="p">)</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<center>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="processonSvg1000" viewBox="248.0 139.0 471.0 276.0" width="471.0" height="276.0"><defs id="ProcessOnDefs1001"><marker id="ProcessOnMarker1018" markerUnits="userSpaceOnUse" orient="auto" markerWidth="16.23606797749979" markerHeight="10.550836550532098" viewBox="-1.0 -1.3763819204711736 16.23606797749979 10.550836550532098" refX="-1.0" refY="3.8990363547948754"><path id="ProcessOnPath1019" d="M12.0 3.8990363547948754L0.0 7.798072709589751V0.0Z" stroke="#323232" stroke-width="2.0" fill="#323232" transform="matrix(1.0,0.0,0.0,1.0,0.0,0.0)" /></marker></defs><g id="ProcessOnG1002"><path id="ProcessOnPath1003" d="M248.0 139.0H719.0V415.0H248.0V139.0Z" fill="none" /><g id="ProcessOnG1004"><g id="ProcessOnG1005" transform="matrix(1.0,0.0,0.0,1.0,381.0,159.0)" opacity="1.0"><path id="ProcessOnPath1006" d="M0.0 0.0L191.0 0.0L191.0 195.0L0.0 195.0Z" stroke="#323232" stroke-width="2.0" stroke-dasharray="none" opacity="1.0" fill="none" /><path id="ProcessOnPath1007" d="M0.0 0.0L191.0 0.0L191.0 40.0L0.0 40.0Z" stroke="#323232" stroke-width="2.0" stroke-dasharray="none" opacity="1.0" fill="#ffffff" /><g id="ProcessOnG1008" transform="matrix(1.0,0.0,0.0,1.0,10.0,10.0)"><text id="ProcessOnText1009" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="16" x="85.5" y="16.4">retained</text></g></g><g id="ProcessOnG1010" transform="matrix(1.0,0.0,0.0,1.0,396.0,218.0)" opacity="1.0"><path id="ProcessOnPath1011" d="M0.0 0.0L161.0 0.0L161.0 111.0L0.0 111.0Z" stroke="#323232" stroke-width="0.0" stroke-dasharray="none" opacity="1.0" fill="none" /><g id="ProcessOnG1012" transform="matrix(1.0,0.0,0.0,1.0,0.0,31.125)"><text id="ProcessOnText1013" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="80.5" y="13.325">全部置0</text><text id="ProcessOnText1014" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="80.5" y="29.575"></text><text id="ProcessOnText1015" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="80.5" y="45.825">【对象存储区域】</text></g></g><g id="ProcessOnG1016"><path id="ProcessOnPath1017" d="M331.0 199.0L355.0 199.0L355.0 199.0L363.7639320225002 199.0" stroke="#323232" stroke-width="2.0" stroke-dasharray="none" fill="none" marker-end="url(#ProcessOnMarker1018)" /></g><g id="ProcessOnG1020" transform="matrix(1.0,0.0,0.0,1.0,268.0,170.0)" opacity="1.0"><path id="ProcessOnPath1021" d="M0.0 0.0L113.0 0.0L113.0 26.0L0.0 26.0Z" stroke="#323232" stroke-width="0.0" stroke-dasharray="none" opacity="1.0" fill="none" /><g id="ProcessOnG1022" transform="matrix(1.0,0.0,0.0,1.0,0.0,4.875)"><text id="ProcessOnText1023" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="56.5" y="13.325">alloc返回指针</text></g></g><g id="ProcessOnG1024" transform="matrix(1.0,0.0,0.0,1.0,572.0,173.0)" opacity="1.0"><path id="ProcessOnPath1025" d="M0.0 0.0L127.0 0.0L127.0 20.0L0.0 20.0Z" stroke="#323232" stroke-width="0.0" stroke-dasharray="none" opacity="1.0" fill="none" /><g id="ProcessOnG1026" transform="matrix(1.0,0.0,0.0,1.0,0.0,1.875)"><text id="ProcessOnText1027" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="63.5" y="13.325">struct obj_layout</text></g></g><g id="ProcessOnG1028" transform="matrix(1.0,0.0,0.0,1.0,382.0,369.0)" opacity="1.0"><path id="ProcessOnPath1029" d="M0.0 0.0L188.0 0.0L188.0 26.0L0.0 26.0Z" stroke="#323232" stroke-width="0.0" stroke-dasharray="none" opacity="1.0" fill="none" /><g id="ProcessOnG1030" transform="matrix(1.0,0.0,0.0,1.0,0.0,4.875)"><text id="ProcessOnText1031" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="94.0" y="13.325">alloc返回对象的内存图</text></g></g></g></g></svg>
</center>
<p><code>NSAllocateObject</code>函数通过调用<code>NSZoneMalloc</code>函数来分配存放对象所需的内存空间，之后将该内存空间置0，最后返回作为对象而使用的指针。那么<code>NSZone</code>又是什么？之前我想让大家移至<a href="http://www.cocoadev.com/index.pl?NSZone">CocoaDev</a>，但是现在作者不在维护了。这里所说的<code>NSZone</code>我做了一下翻译：</p>
<p><em>从大体上来说，<code>NSZone</code>是Apple分配和释放内存的一种方式，它不是一个对象，而是使用C语言中的结构体来存储关于对象的内存管理信息。基本上，不需开发者去管理它，<strong>Cocoa Application</strong>使用一个默认的<code>NSZone</code>来对应用的对象进行管理。但是当默认的<code>NSZone</code>里面管理了大量数据的时候，你会想要一个自己控制的<code>NSZone</code>。中重视和，大量对象的释放可能会导致严重的<strong>内存碎片化</strong>，Cocoa本身有做过优化，每次<code>alloc</code>时会尝试着填满内存空隙，但如此开销会很大。于是，为了优化效率，你可以自己创建<code>NSZone</code>，当你有大量的<code>alloc</code>请求时，就全部转移到指定的<code>NSZone</code>，便可减少大量的时间开销。而且，使用<code>NSZone</code>还可以一次性的将你创建在<code>NSZone</code>的东西全部清除，避免逐个<code>dealloc</code>。</em></p>
<p>熟悉C或者C++的读者，读过以后可以立马反应到，这其实就是一个官方封装的<strong>内存池</strong>。无论是<strong>优化内存碎片化</strong>还是<strong>对象统一释放</strong>，都是内存池的显著特点。总的来说，当你需要大量创建对象的时候，使用<code>NSZone</code>能提高效率的。在<strong>Cocoabuilder</strong>中，有一篇叫<em><a href="http://www.cocoabuilder.com/archive/cocoa/65056-what-an-nszone.html#65056">what’s an NSZone?</a></em>的帖子中，Timothy J. Wood写道：由于历史原因，现在已经不能创建一个真正的<code>NSZone</code>，而是在Main Zone中创建一个Child Zone，这样不会使存储单元过度碎片化。发表日期是2002年，也就是说，Cocoa很早之前就已经注意到内存碎片的危险，而改善了Zone方法。</p>
<p>再来说一下<code>retain</code>和<code>release</code>。我们也从<strong>GNUstep</strong>源码入手：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="nb">id</span><span class="p">)</span> <span class="n">retain</span> <span class="p">{</span>
    <span class="no">NSIncrementExtraRefCount</span><span class="p">(</span><span class="nb">self</span><span class="p">);</span>
    <span class="k">return</span> <span class="nb">self</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">inline</span> <span class="n">void</span> <span class="no">NSIncrementExtraRefCount</span><span class="p">(</span><span class="nb">id</span> <span class="n">anObject</span><span class="p">)</span> <span class="p">{</span>
    <span class="sr">//</span> <span class="err">判断计数最大值</span>
    <span class="k">if</span> <span class="p">(((</span><span class="n">struct</span> <span class="n">obj_layout</span> <span class="o">*</span><span class="p">)</span><span class="n">anObject</span><span class="p">)</span><span class="o">[-</span><span class="mi">1</span><span class="o">].</span><span class="n">retained</span> <span class="o">==</span> <span class="no">UINT_MAX</span> <span class="o">-</span> <span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
        <span class="o">[</span><span class="no">NSException</span> <span class="k">raise</span><span class="p">:</span> <span class="no">NSInternalInconsistencyException</span> 
                    <span class="nb">format</span><span class="p">:</span> <span class="err">@</span><span class="s2">&quot;NSIncrementExtraRefCount() asked to increment too far&quot;</span><span class="o">]</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="p">((</span><span class="n">struct</span> <span class="n">obj_layout</span> <span class="o">*</span><span class="p">)</span><span class="n">anObject</span><span class="p">)</span><span class="o">[-</span><span class="mi">1</span><span class="o">].</span><span class="n">retained</span><span class="o">++</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<p>大概扫一遍代码，其实我们只是对计数的上线做了一个判断。<code>UINT_MAX - 1</code>这是个什么东西呢。直接敲到<strong>Xcode</strong>中发现这个值得<code>18446744073709551615</code>，转换成二进制<code>1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111</code>，也就是我们常说的<script type="math/tex">2^{64} - 1</script>。这个值也就是<code>-1</code>在内存当中的补码存储形式。记住这个值，我们后面还会遇到。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span> <span class="n">release</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(</span><span class="no">NSDecrementExtraRefCountWasZero</span><span class="p">(</span><span class="nb">self</span><span class="p">))</span> <span class="p">{</span>
        <span class="o">[</span><span class="nb">self</span> <span class="n">dealloc</span><span class="o">]</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
<span class="n">bool</span> <span class="no">NSDerementExtraRefCountWasZero</span><span class="p">(</span><span class="nb">id</span> <span class="n">anObject</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">if</span> <span class="p">(((</span><span class="n">strcut</span> <span class="n">obj_layout</span> <span class="o">*</span><span class="p">)</span><span class="n">anObject</span><span class="p">)</span><span class="o">[-</span><span class="mi">1</span><span class="o">].</span><span class="n">retained</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
        <span class="k">return</span> <span class="no">YES</span><span class="p">;</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
        <span class="p">((</span><span class="n">struct</span> <span class="n">obj_layout</span> <span class="o">*</span><span class="p">)</span> <span class="n">anObject</span><span class="p">)</span><span class="o">[-</span><span class="mi">1</span><span class="o">].</span><span class="n">retained</span> <span class="o">--</span><span class="p">;</span>
        <span class="k">return</span> <span class="no">NO</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span></code></pre></div>
<p><code>release</code>类比于上面的<code>retain</code>也就好理解多了，这里我们只要有一个下限判断，如果计数等于0的时候，调用<code>dealloc</code>实例方法，废弃对象。</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="n">void</span><span class="p">)</span> <span class="n">dealloc</span> <span class="p">{</span>
    <span class="no">NSDeallocateobject</span><span class="p">(</span><span class="nb">self</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">inline</span> <span class="n">void</span> <span class="no">NSDeallocateObject</span><span class="p">(</span><span class="nb">id</span> <span class="n">anObject</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">struct</span> <span class="n">obj_layout</span> <span class="o">*</span><span class="n">o</span> <span class="o">=</span> <span class="o">&amp;</span><span class="p">((</span><span class="n">strcut</span> <span class="n">obj_layout</span> <span class="o">*</span><span class="p">)</span><span class="n">anObject</span><span class="p">)</span><span class="o">[-</span><span class="mi">1</span><span class="o">]</span><span class="p">;</span>
    <span class="n">free</span><span class="p">(</span><span class="n">o</span><span class="p">);</span>
<span class="p">}</span></code></pre></div>
<p>上述代码仅废弃了由<code>alloc</code>分配的内存块。最后简单看一下<code>retainCount</code>的实现</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">-</span> <span class="p">(</span><span class="no">NSUInteger</span><span class="p">)</span> <span class="n">retainCount</span> <span class="p">{</span>
    <span class="k">return</span> <span class="no">NSExtraRefCount</span><span class="p">(</span><span class="nb">self</span><span class="p">)</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="n">inline</span> <span class="no">NSUInteger</span> <span class="no">NSExtraRefCount</span><span class="p">(</span><span class="nb">id</span> <span class="n">anObject</span><span class="p">)</span> <span class="p">{</span>
    <span class="k">return</span> <span class="p">((</span><span class="n">struct</span> <span class="n">obj_layout</span> <span class="o">*</span><span class="p">)</span><span class="n">anObject</span><span class="p">)</span><span class="o">[-</span><span class="mi">1</span><span class="o">].</span><span class="n">retained</span><span class="p">;</span>
<span class="p">}</span></code></pre></div>
<center>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="processonSvg1000" viewBox="173.0 239.0 453.0 299.0" width="453.0" height="299.0"><defs id="ProcessOnDefs1001"><linearGradient id="ProcessOnLinearGradient1007" x1="0.9469983318002789" y1="0.9928809777420872" x2="0.4119770267643943" y2="0.0"><stop id="ProcessOnStop1008" stop-opacity="1.0" stop-color="#9dd7ed" offset="0.0" /><stop id="ProcessOnStop1009" stop-opacity="1.0" stop-color="#899dc0" offset="1.0" /></linearGradient><linearGradient id="ProcessOnLinearGradient1014" x1="0.9469983318002789" y1="0.8982876587814846" x2="0.4119770267643943" y2="0.0"><stop id="ProcessOnStop1015" stop-opacity="1.0" stop-color="#f5ecba" offset="0.0" /><stop id="ProcessOnStop1016" stop-opacity="1.0" stop-color="#f4d000" offset="1.0" /></linearGradient><marker id="ProcessOnMarker1019" markerUnits="userSpaceOnUse" orient="auto" markerWidth="16.23606797749979" markerHeight="10.550836550532098" viewBox="-1.0 -1.3763819204711736 16.23606797749979 10.550836550532098" refX="-1.0" refY="3.8990363547948754"><path id="ProcessOnPath1020" d="M12.0 3.8990363547948754L0.0 7.798072709589751V0.0Z" stroke="#323232" stroke-width="2.0" fill="#323232" transform="matrix(1.0,0.0,0.0,1.0,0.0,0.0)" /></marker><marker id="ProcessOnMarker1027" markerUnits="userSpaceOnUse" orient="auto" markerWidth="16.23606797749979" markerHeight="10.550836550532098" viewBox="-1.0 -1.3763819204711736 16.23606797749979 10.550836550532098" refX="-1.0" refY="3.8990363547948754"><path id="ProcessOnPath1028" d="M12.0 3.8990363547948754L0.0 7.798072709589751V0.0Z" stroke="#323232" stroke-width="2.0" fill="#323232" transform="matrix(1.0,0.0,0.0,1.0,0.0,0.0)" /></marker></defs><g id="ProcessOnG1002"><path id="ProcessOnPath1003" d="M173.0 239.0H626.0V538.0H173.0V239.0Z" fill="none" /><g id="ProcessOnG1004"><g id="ProcessOnG1005" transform="matrix(1.0,0.0,0.0,1.0,328.0,289.0)" opacity="1.0"><path id="ProcessOnPath1006" d="M0.0 0.0L176.0 0.0L176.0 80.0L0.0 80.0L0.0 0.0Z" stroke="#0b6cc3" stroke-width="2.0" stroke-dasharray="none" opacity="1.0" fill="url(#ProcessOnLinearGradient1007)" /><g id="ProcessOnG1010" transform="matrix(1.0,0.0,0.0,1.0,10.0,31.875)"><text id="ProcessOnText1011" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="78.0" y="13.325">struct obj_layout</text></g></g><g id="ProcessOnG1012" transform="matrix(1.0,0.0,0.0,1.0,328.0,369.0)" opacity="1.0"><path id="ProcessOnPath1013" d="M0.0 0.0L176.0 0.0L176.0 99.0L0.0 99.0Z" stroke="#dc5712" stroke-width="2.0" stroke-dasharray="none" opacity="1.0" fill="url(#ProcessOnLinearGradient1014)" /></g><g id="ProcessOnG1017"><path id="ProcessOnPath1018" d="M239.0 370.0L282.0 370.0L282.0 370.0L309.7639320225002 370.0" stroke="#323232" stroke-width="2.0" stroke-dasharray="none" fill="none" marker-end="url(#ProcessOnMarker1019)" /></g><g id="ProcessOnG1021" transform="matrix(1.0,0.0,0.0,1.0,193.0,322.0)" opacity="1.0"><path id="ProcessOnPath1022" d="M0.0 0.0L124.0 0.0L124.0 47.0L0.0 47.0Z" stroke="#323232" stroke-width="0.0" stroke-dasharray="none" opacity="1.0" fill="none" /><g id="ProcessOnG1023" transform="matrix(1.0,0.0,0.0,1.0,0.0,15.375)"><text id="ProcessOnText1024" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="62.0" y="13.325">anObject对象指针</text></g></g><g id="ProcessOnG1025"><path id="ProcessOnPath1026" d="M503.5621532777801 370.53570957044417L534.0 370.53570957044417L534.0 259.0L416.0 259.0L416.0 273.7639320225002" stroke="#323232" stroke-width="2.0" stroke-dasharray="none" fill="none" marker-end="url(#ProcessOnMarker1027)" /></g><g id="ProcessOnG1029" transform="matrix(1.0,0.0,0.0,1.0,538.0,300.0)" opacity="1.0"><path id="ProcessOnPath1030" d="M0.0 0.0L68.0 0.0L68.0 50.0L0.0 50.0Z" stroke="#323232" stroke-width="0.0" stroke-dasharray="none" opacity="1.0" fill="none" /><g id="ProcessOnG1031" transform="matrix(1.0,0.0,0.0,1.0,0.0,16.875)"><text id="ProcessOnText1032" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="34.0" y="13.325">访问头部</text></g></g><g id="ProcessOnG1033" transform="matrix(1.0,0.0,0.0,1.0,287.5,474.0)" opacity="1.0"><path id="ProcessOnPath1034" d="M0.0 0.0L257.0 0.0L257.0 44.0L0.0 44.0Z" stroke="#323232" stroke-width="0.0" stroke-dasharray="none" opacity="1.0" fill="none" /><g id="ProcessOnG1035" transform="matrix(1.0,0.0,0.0,1.0,0.0,13.875)"><text id="ProcessOnText1036" fill="#000000" font-weight="normal" font-style="normal" text-decoration="blink" font-family="微软雅黑" text-anchor="middle" font-size="13" x="128.5" y="13.325">通过对象访问对象内存头部</text></g></g></g></g></svg>
</center>
<p>如上示意图，指向<code>struct obj_layout</code>的指针减去<code>struct obj_layout</code>大小的地址，即可找到访问对象内存头部。因为分配时全部置0，由<code>NSExtraRefCount(self) + 1</code>得出，<code>retainCount</code>为1，可以推测出，<code>retain</code>方法使<code>retained</code>属性加1，而<code>release</code>减1。</p>
<p>在苹果的官方实现中，<code>__CFDoExternRefoperation</code>函数中可以发现苹果采用了一个<strong>引用计数表</strong>，结构类似于<strong>散列</strong>，来管理内存块以及引用计数。这也是官方代码和GNUstep的一点区别。（官方代码可以从<a href="http://www.opensource.apple.com/source/objc4">这里</a>查看。）</p>
<p>更多的了解苹果源码可以参考书籍<em><a href="https://book.douban.com/subject/24720270/">Pro multithreading and memory management for iOS and OS X</a></em></p>
<h2 id="when-to-use--retaincount">When to use -retainCount</h2>
<blockquote>
  <p>When to use -retainCount</p>
</blockquote>
<p>这个子标题的答案是什么呢？最后揭晓。</p>
<p>初学Reference Counting，我们会使用<code>retainCount</code>来进行一些实验。例如这个代码：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">NSString</span> <span class="o">*</span><span class="n">string</span> <span class="o">=</span> <span class="err">@</span><span class="s2">&quot;Some&quot;</span><span class="p">;</span>
<span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;retainCount: %lu&quot;</span><span class="p">,</span> <span class="o">[</span><span class="n">string</span> <span class="n">retainCount</span><span class="o">]</span><span class="p">);</span>
<span class="no">NSNumber</span> <span class="o">*</span><span class="n">number1</span> <span class="o">=</span> <span class="err">@</span><span class="mi">1</span><span class="p">;</span>
<span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;retainCount: %lu&quot;</span><span class="p">,</span> <span class="o">[</span><span class="n">number1</span> <span class="n">retainCount</span><span class="o">]</span><span class="p">);</span>
<span class="no">NSNumber</span> <span class="o">*</span><span class="n">number2</span> <span class="o">=</span> <span class="err">@</span><span class="mi">3</span><span class="o">.</span><span class="mi">14</span><span class="p">;</span>
<span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;retainCount: %lu&quot;</span><span class="p">,</span> <span class="o">[</span><span class="n">number2</span> <span class="n">retainCount</span><span class="o">]</span><span class="p">);</span></code></pre></div>
<p>输出结果为如下：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="mi">2016</span><span class="o">-</span><span class="mo">06</span><span class="o">-</span><span class="mi">24</span> <span class="mi">10</span><span class="p">:</span><span class="mi">18</span><span class="p">:</span><span class="mi">57</span><span class="o">.</span><span class="mi">797</span> <span class="n">text</span><span class="o">[</span><span class="mi">43373</span><span class="p">:</span><span class="mi">4020361</span><span class="o">]</span> <span class="ss">retainCount</span><span class="p">:</span> <span class="mi">18446744073709551615</span>
<span class="mi">2016</span><span class="o">-</span><span class="mo">06</span><span class="o">-</span><span class="mi">24</span> <span class="mi">10</span><span class="p">:</span><span class="mi">18</span><span class="p">:</span><span class="mi">57</span><span class="o">.</span><span class="mi">798</span> <span class="n">text</span><span class="o">[</span><span class="mi">43373</span><span class="p">:</span><span class="mi">4020361</span><span class="o">]</span> <span class="ss">retainCount</span><span class="p">:</span> <span class="mi">9223372036854775807</span>
<span class="mi">2016</span><span class="o">-</span><span class="mo">06</span><span class="o">-</span><span class="mi">24</span> <span class="mi">10</span><span class="p">:</span><span class="mi">18</span><span class="p">:</span><span class="mi">57</span><span class="o">.</span><span class="mi">798</span> <span class="n">text</span><span class="o">[</span><span class="mi">43373</span><span class="p">:</span><span class="mi">4020361</span><span class="o">]</span> <span class="ss">retainCount</span><span class="p">:</span> <span class="mi">1</span></code></pre></div>
<p>第一次看到这些值的时候，都会大声惊叹：“Why？”其实，第一个值是之前见过的<script type="math/tex">2^{64} - 1</script>，第二个值是<script type="math/tex">2^{63} - 1</script>。为什么会这样，原因是因为我们的编译器将其实现为<em>Singleton Object</em>（也就是我们常说的<strong>单例对象</strong>）。编译时编译器会把其对象所表示的数据放到应用程序的二进制文件中，这样的话运行程序就可以直接使用，无需再创建<code>NSString</code>对象。这是一种编译优化手段，我们称之为<strong>编译器常量</strong>（Compile-time Constant）。而对于<code>NSNumber</code>来说，它使用了一种<strong>标签指针</strong>（Tagged Pointer）机制来标注特定类型的数值，这不依赖与<code>NSNumber</code>对象，而是把与数值有关的全部消息都放在标签指针中，并对它执行响应操作。而这些标签指针系统会在<strong>消息派发阶段</strong>（objc_msgSend）来检测标签指针，从而获得数据信息。<code>NSNumber</code>这种优化只在某些场合使用，比如例中的浮点数对象就没有优化，所以其保留计数为1。</p>
<p>我们来验证一下<code>NSString</code>的单例存储：</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">NSString</span> <span class="o">*</span><span class="n">a</span> <span class="o">=</span> <span class="err">@</span><span class="s2">&quot;gua&quot;</span><span class="p">;</span>
<span class="no">NSString</span> <span class="o">*</span><span class="n">b</span> <span class="o">=</span> <span class="err">@</span><span class="s2">&quot;gua&quot;</span><span class="p">;</span>
<span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;%p&quot;</span><span class="p">,</span> <span class="n">a</span><span class="p">);</span>
<span class="no">NSLog</span><span class="p">(</span><span class="err">@</span><span class="s2">&quot;%p&quot;</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span></code></pre></div>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="mi">2016</span><span class="o">-</span><span class="mo">06</span><span class="o">-</span><span class="mi">24</span> <span class="mi">10</span><span class="p">:</span><span class="mi">37</span><span class="p">:</span><span class="mi">39</span><span class="o">.</span><span class="mi">511</span> <span class="n">text</span><span class="o">[</span><span class="mi">43524</span><span class="p">:</span><span class="mi">4042012</span><span class="o">]</span> <span class="mh">0x106ddb050</span>
<span class="mi">2016</span><span class="o">-</span><span class="mo">06</span><span class="o">-</span><span class="mi">24</span> <span class="mi">10</span><span class="p">:</span><span class="mi">37</span><span class="p">:</span><span class="mi">39</span><span class="o">.</span><span class="mi">512</span> <span class="n">text</span><span class="o">[</span><span class="mi">43524</span><span class="p">:</span><span class="mi">4042012</span><span class="o">]</span> <span class="mh">0x106ddb050</span></code></pre></div>
<p>结果发现我们一旦有一个<code>NSString</code>对象，并且它的值为@”gua”的时候，其指针指向的地址单元永远是一致的。倘若不是单例对象，则不可能出现相同地址。可能你会纠结于那么不同的单例对象为何引用计数还会不相同？其实这里我们无需关注这个问题，我们只要肯定的是，<strong>单例对象的引用计数始终不会改变</strong>。其实这也反应这么一种思想：<strong>我们不应该总是依赖保留计数的具体值来编码</strong>。</p>
<p>其实<code>retainCount</code>这个属性很早就被苹果公司放弃使用了。两个原因：</p>
<ul>
  <li><code>retainCount</code>没有考虑后续的自动释放操作，只是不停地通过释放操作来降低保留计数，直至是对象为系统回收。假如此对象在自动释放池里，那么稍后系统清空是还要继续释放，导致crash。</li>
  <li><code>retainCount</code>有回收不确定性。<code>retainCount</code>可能永远不得0，因为有时系统会优化对象的释放行为，在保留计数是1的时候，就回收了。只有当系统不打算优化时，计数值才会递减至0。</li>
</ul>
<p>苹果的<a href="https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/index.html#//apple_ref/occ/intfm/NSObject/retainCount">官方文档</a>也对其没有价值有了很详尽的说明。在官方引入ARC时，毫不犹豫的将其放弃，我们就更不应该使用了。</p>
<p>综上，回答标题问题：<strong><a href="http://sdarlington.github.io/">When to use -retainCount？</a> The answer is Never!</strong></p>
<h2 id="end">End</h2>
<p>这篇博文只是简单的给出了引用计数是如何工作的，以及避免使用<code>retainCount</code>的原因，后续我希望进一步探究<strong>iOS内存管理</strong>的其他细节问题，希望读者与笔者一样，共同探究问题，挖掘本质。倘若文章有错误，望指出继而共勉。</p>
<hr />
<p><a href="http://stackoverflow.com/questions/37985945/lots-of-problems-about-nsstring-reference-count">lots-of-problems-about-nsstring-reference-count</a></p>
  <footer class="post-footer">
    <section class="author">
      <h4>Desgard_Duan</h4>
      <p>I write many code to write less code.💻</p>
    </section>
<aside class="share">
  <h4>Share this.</h4>
  <a href="http://twitter.com/share?text=《Reference Counting Brief Analysis》 -- Guardia · 瓜地&amp;url=https://www.desgard.com/Reference-Count-Analysis/"
  onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-twitter-square"></i>
  </a>
  <a href="http://v.t.sina.com.cn/share/share.php?url=https://www.desgard.com/Reference-Count-Analysis/&amp;title=《Reference Counting Brief Analysis》 —— Guardia · 瓜地" onclick="window.open(this.href, 'twitter-share', 'width=550,height=235');return false;">
    <i class="fa fa-weibo"></i>
  </a>
</aside>
      <hr>
<aside id="comments" class="disqus">
  <div id="container"></div>
  <!-- <link rel="stylesheet" href="/assets/css/gitment.css">
  <script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
  <script>
  var gitment = new Gitment({
    id: "https://www.desgard.com/Reference-Count-Analysis/", 
    owner: 'Desgard',
    repo: 'desgard.github.com',
    oauth: {
      client_id: 'e2612df42f3f2a83e71c',
      client_secret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
    },
  })
  gitment.render('container')
  </script> -->
<link rel="stylesheet" href="/assets/css/gitalk.css">
<script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>
<div id='gitalk-container'></div>
<script>
    const gitalk = new Gitalk({
        id: "https://www.desgard.com/Reference-Count-Analysis/", 
        clientID: 'e2612df42f3f2a83e71c',
        clientSecret: 'b53e85b314bb24a6d06773e48bbb62a4de3b8b3a',
        repo: 'desgard.github.com',
        owner: 'Desgard',
        admin: ['Desgard'],
        // facebook-like distraction free mode
        distractionFreeMode: false
    })
    gitalk.render('gitalk-container')
</script>
</aside>
  </footer>
</article>
    </main>
<footer class="footer">
  <div class="container">
    <ul class="icons">
      <li>
        <a href="https://github.com/desgard" class="icon-github" target="_blank">
          <i class="fa fa-github"></i>
        </a>
      </li>
      <li>
        <a href="https://www.facebook.com/desgard.duan" class="icon-facebook" target="_blank">
          <i class="fa fa-facebook"></i>
        </a>
      </li>
      <li>
        <a href="https://twitter.com/Desgard_Duan" class="icon-twitter" target="_blank">
          <i class="fa fa-twitter"></i>
        </a>
      </li>
      <li>
        <a href="https://stackoverflow.com/users/6119149/desgard-duan" class="icon-github" target="_blank">
          <i class="fa fa-stack-overflow"></i>
        </a>
      </li>
      <li>
        <a href="https://weibo.com/desgard" class="icon-instagram" target="_blank">
          <i class="fa fa-weibo"></i>
        </a>
      </li>
    </ul>
    <p>
      <q>I write many code to write less code.💻</q>
      <small>– Gua</small>
    </p>
    <small class="clearfix">
      Powered by <a href="http://jekyllrb.com" target="_blank">Jekyll</a> • <a href="https://github.com/desgard" target="_blank">Open source <i class="fa fa-heart"></i></a>
    </small>
  </div>
</footer>
<a class="scroll-up fa fa-chevron-up bounce" href="#" data-position="0"></a>
<div id="modalSearch" class="modal">
  <div class="modal__overlay"></div>
  <div class="modal__content">
    <a href="#!" class="modal-close" data-modal-close>&times;</a>
    <div class="search-container">
      <input type="text" id="search-input" placeholder="Search articles">
      <ul id="results-container"></ul>
    </div>
  </div>
</div>
    <script src="/assets/main-52d417e8a6ff9f5b168386d37c96338a.js"></script>
  </body>
  <script>
    var link = "" ;
    var os = function() {  
      var ua = navigator.userAgent,  
      isWindowsPhone = /(?:Windows Phone)/.test(ua),  
      isSymbian = /(?:SymbianOS)/.test(ua) || isWindowsPhone,   
      isAndroid = /(?:Android)/.test(ua),   
      isFireFox = /(?:Firefox)/.test(ua),   
      isChrome = /(?:Chrome|CriOS)/.test(ua),  
      isTablet = /(?:iPad|PlayBook)/.test(ua) || (isAndroid && !/(?:Mobile)/.test(ua)) || (isFireFox && /(?:Tablet)/.test(ua)),  
      isPhone = /(?:iPhone)/.test(ua) && !isTablet,  
      isPc = !isPhone && !isAndroid && !isSymbian;  
      return {  
        isTablet: isTablet,  
        isPhone: isPhone,  
        isAndroid : isAndroid,  
        isPc : isPc  
      };  
    }();  
    if (link.length > 0) {
      if (os.isAndroid || os.isPhone) {
        location.replace(link);
      }
    }
  </script>
</html>